#include "AudioProcessor.h"
#include "core/gamestream/GameStreamManager.h"
#include "core/gamestream/XComponentBackend.h"
#include <cstdint>

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN  0x0
#define LOG_TAG     "AudioRendererCallbacks"
#include <hilog/log.h>


using AudioRendererCallbacks = AudioProcessor::AudioRendererCallbacks;

int32_t AudioRendererCallbacks::OnWriteData(OH_AudioRenderer *renderer, void *userData, void *buffer, int32_t length) {
    if constexpr (DEBUG) {
        OH_LOG_DEBUG(LOG_APP, "Audio renderer request write data length %{public}d", length);
    }
    auto &self = *reinterpret_cast<AudioProcessor*>(userData);
    for (int64_t filled = 0; filled < length; ) {
        std::unique_lock l(self.renderMtx);
        self.renderReadyCond.wait(l, [&] { return !self.renderQueue.empty() || self.xcomp.stopFlag; });
        if (self.xcomp.stopFlag) {
            return AUDIO_DATA_CALLBACK_RESULT_INVALID;
        }
        auto &decoded = self.renderQueue.front();
        if constexpr (DEBUG) {
            OH_LOG_DEBUG(LOG_APP, "Got decoded buffer of length %{public}d, remaining %{public}d, queuing buffers %{public}d",
                decoded.lengthInBytes, decoded.remainingBytes(), self.renderQueue.size());
        }
        const int64_t toSubmit = std::min<int64_t>({length - filled, decoded.remainingBytes()});
        std::memcpy(reinterpret_cast<uint8_t*>(buffer) + filled, decoded.currentData(), toSubmit);
        decoded.advanceBytes(toSubmit);
        if constexpr (DEBUG) {
            OH_LOG_DEBUG(LOG_APP, "Consumed %{public}d of buffer, remaining %{public}d", toSubmit, decoded.remainingBytes());
        }
        if (decoded.remainingBytes() <= 0) {
            if constexpr (DEBUG) {
                OH_LOG_DEBUG(LOG_APP, "Fully consumed a decoded sample group of length %{public}d", decoded.lengthInBytes);
            }
            self.renderQueue.pop_front();
        }
        filled += toSubmit;
    }
    if constexpr (DEBUG) {
        OH_LOG_DEBUG(LOG_APP, "Audio renderer prepared data %{public}d", length);
    }
    return AUDIO_DATA_CALLBACK_RESULT_VALID;
}

int32_t AudioRendererCallbacks::OnStreamEvent(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Event event) {
    OH_LOG_INFO(LOG_APP, "Audio renderer receiving stream event %{public}d", event);
    // TODO handle routing changed
    return AUDIO_DATA_CALLBACK_RESULT_VALID;
}

int32_t AudioRendererCallbacks::OnInterruptEvent(OH_AudioRenderer *renderer, void *userData, OH_AudioInterrupt_ForceType type, OH_AudioInterrupt_Hint hint) {
    OH_LOG_INFO(LOG_APP, "Audio renderer receiving interrupt event type %{public}d hint %{public}d", type, hint);
    // TODO handle audio interrupt
    return AUDIO_DATA_CALLBACK_RESULT_VALID;
}

int32_t AudioRendererCallbacks::OnError(OH_AudioRenderer *render, void *userData, OH_AudioStream_Result error) {
    OH_LOG_ERROR(LOG_APP, "Audio renderer error %{public}d", error);
    GameStreamManager::emitEvent("AudioRenderError", {static_cast<int32_t>(error)});
    return AUDIO_DATA_CALLBACK_RESULT_VALID;
}
